package demos.consumer;

import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class MyBlockingQueue1<T> {
    public static void main(String[] args) {
        MyBlockingQueue1<Integer> queue = new MyBlockingQueue1<>(3);
        new Thread(() -> {
            for (int i = 0; i < 100; ++i) {
                queue.put(i);
            }
        }).start();

        new Thread(() -> {
            for (int i = 0; i < 100; ++i) {
                queue.take();
            }
        }).start();

        new LinkedBlockingQueue<Integer>();
    }

    private Queue<T> queue;
    private final int capacity;
    private int size;

    private Lock lock = new ReentrantLock();
    private Condition notFull = lock.newCondition();
    private Condition notEmpty = lock.newCondition();

    public MyBlockingQueue1(int capacity) {
        this.capacity = capacity;
        queue = new LinkedList<>();
    }

    public void put(T data) {
        lock.lock();
        try {
            while (size == capacity) {
                notFull.await();
            }
            System.out.println("put in");
            queue.add(data);
            size++;
            notEmpty.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public T take() {
        T e = null;
        lock.lock();
        try {
            while (size == 0) {
                notEmpty.await();
            }
            System.out.println("    take out");
            e = queue.remove();
            size--;
            notFull.signal();
        } catch (InterruptedException interruptedException) {
            interruptedException.printStackTrace();
        } finally {
            lock.unlock();
        }
        return e;
    }
}

